﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

// Параметры для вызова ВнешниеКомпонентыСервер.ПодключитьКомпоненту.
//
// Возвращаемое значение:
//  Структура:
//    * ИдентификаторыСозданияОбъектов - Массив из Строка - идентификаторы экземпляров модуля объекта,
//              используется только для компонент, у которых есть несколько идентификаторов создания объектов.
//              При задании параметр Идентификатор будет использоваться только для определения компоненты.
//    * Изолированно - Булево, Неопределено - (по умолчанию Неопределено) если Истина, компонента будет подключена
//              изолированно, в этом случае внешняя компонента загружается в отдельный процесс операционной системы;
//              Ложь - в этом случае внешняя компонента будет выполняться в том же процессе операционной системы,
//              который выполняет код встроенного языка; Неопределено - поддерживается поведение платформы по умолчанию:
//              не изолированно - если компонентой поддерживается только этот режим, изолированно - в остальных случаях.
//              См. https://its.1c.ru/db/v83doc#bookmark:dev:TI000001866
//    * ПолноеИмяМакета - Строка - полное имя макета конфигурации с ZIP-архивом, в котором храниться внешняя компонента.
//
//
Функция ПараметрыПодключения() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("ИдентификаторыСозданияОбъектов", Новый Массив);
	Параметры.Вставить("Изолированно", Ложь);
	Параметры.Вставить("ПолноеИмяМакета");
	
	Возврат Параметры;
	
КонецФункции

// Подключает на сервере 1С:Предприятия внешнюю компоненту из хранилища внешних компонент,
// выполненную по технологии Native API или COM.
// В модели сервиса разрешено только подключение общих внешних компонент, одобренных администратором сервиса.
//
// Параметры:
//  Идентификатор - Строка - идентификатор объекта внешней компоненты.
//  Версия        - Строка - версия компоненты.
//  ПараметрыПодключения - см. ПараметрыПодключения.
//
// Возвращаемое значение:
//   Структура - результат подключения компоненты:
//     * Подключено - Булево - признак подключения;
//     * ПодключаемыйМодуль - ОбъектВнешнейКомпоненты - экземпляр объекта внешней компоненты;
//                          - ФиксированноеСоответствие из КлючИЗначение - экземпляры объектов внешней компоненты,
//                            указанные в ПараметрыПодключения.ИдентификаторыСозданияОбъектов:
//                            ** Ключ - Строка - идентификатор,
//                            ** Значение - ОбъектВнешнейКомпоненты - экземпляр объекта внешней компоненты.
//     * ОписаниеОшибки - Строка - краткое описание ошибки. 
//
Функция ПодключитьКомпоненту(Знач Идентификатор, Версия = Неопределено, ПараметрыПодключения = Неопределено) Экспорт
	
	Если ПараметрыПодключения = Неопределено Тогда
		Изолированно = Ложь;
	Иначе
		Изолированно = ПараметрыПодключения.Изолированно;
	КонецЕсли;
	
	РезультатПроверки = ВнешниеКомпонентыСлужебный.ПроверитьПодключениеКомпоненты(Идентификатор, Версия, ПараметрыПодключения);
	Если Не ПустаяСтрока(РезультатПроверки.ОписаниеОшибки) Тогда
		Результат = Новый Структура;
		Результат.Вставить("Подключено", Ложь);
		Результат.Вставить("ПодключаемыйМодуль", Неопределено);
		Результат.Вставить("Версия", Версия);
		Результат.Вставить("ОписаниеОшибки", РезультатПроверки.ОписаниеОшибки);
		Возврат Результат;
	КонецЕсли;
	
	Возврат ОбщегоНазначения.ПодключитьКомпонентуПоИдентификатору(
		РезультатПроверки.Идентификатор, РезультатПроверки.Местоположение, Изолированно);
	
КонецФункции

#Область ДляВызоваИзДругихПодсистем

// ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент

// Возвращает таблицу описаний внешних компонент
//
// Параметры:
//  Вариант - Строка - может принимать значения:
//    "ДляОбновления" - компоненты только из справочника с установленным признаком ОбновлятьСПортала1СИТС.
//    "ДляЗагрузки"   - компоненты, используемые в конфигурации.
//    "Поставляемые"  - для определения поставляемых компонент в модели сервиса.
//
// Возвращаемое значение:
//   - см. ПолучениеВнешнихКомпонент.ОписаниеВнешнихКомпонент
//   - Массив - идентификаторы поставляемых компонент, если Вариант = "Поставляемые"
//
Функция ИспользуемыеКомпоненты(Вариант) Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент") Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Действие недоступно, т.к. отсутствует подсистема ""%1"".'"),
			"ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент");
	КонецЕсли;
	
	Если Вариант = "Поставляемые" Тогда
		Возврат ВнешниеКомпонентыСлужебный.ПоставляемыеКомпоненты();
	КонецЕсли;
	
	ДанныеВнешнихКомпонент = ВнешниеКомпонентыСлужебный.ДанныеВнешнихКомпонент(Вариант);
	
	МодульПолучениеВнешнихКомпонент = ОбщегоНазначения.ОбщийМодуль("ПолучениеВнешнихКомпонент");
	ОписаниеВнешнихКомпонент = МодульПолучениеВнешнихКомпонент.ОписаниеВнешнихКомпонент();
	
	Для Каждого ОписаниеКомпоненты Из ДанныеВнешнихКомпонент Цикл
		НоваяСтрока = ОписаниеВнешнихКомпонент.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, ОписаниеКомпоненты);
	КонецЦикла;
	
	Возврат ОписаниеВнешнихКомпонент;
	
КонецФункции

// Выполняет обновление внешних компонент.
//
// Параметры:
//  ДанныеВнешнихКомпонент - ТаблицаЗначений - сведения об обновляемых компонентах:
//    * Идентификатор - Строка - идентификатор.
//    * Версия - Строка - версия.
//    * ДатаВерсии - Строка - дата версии.
//    * Наименование - Строка - наименование.
//    * ИмяФайла - Строка - имя файла.
//    * АдресФайла - Строка - адрес файла.
//    * КодОшибки - Строка - код ошибки.
//  АдресРезультата - Строка - адрес временного хранилища.
//      Если указан, в него будет помещено описание результата операции.
//      Структура:
//       # Результат - Булево - Ложь, если есть ошибки
//       # Ошибки - Соответствие:
//         ## Ключ - Строка - Идентификатор
//         ## Значение - Строка - СообщениеОбОшибке
//       # Успех - Соответствие:
//         ## Ключ - Строка - Идентификатор
//         ## Значение - Строка - СообщениеОбОшибке
//
Процедура ОбновитьВнешниеКомпоненты(ДанныеВнешнихКомпонент, АдресРезультата = Неопределено) Экспорт
	
	РезультатВыполнения = Новый Структура;
	РезультатВыполнения.Вставить("Результат", Ложь);
	РезультатВыполнения.Вставить("Ошибки", Новый Соответствие);
	РезультатВыполнения.Вставить("Успех", Новый Соответствие);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент") Тогда
	
		Запрос = Новый Запрос;
		Запрос.Текст =
			"ВЫБРАТЬ
			|	ВнешниеКомпоненты.Ссылка КАК Ссылка,
			|	ВнешниеКомпоненты.Идентификатор КАК Идентификатор,
			|	ВнешниеКомпоненты.Версия КАК Версия,
			|	ВнешниеКомпоненты.ДатаВерсии КАК ДатаВерсии
			|ИЗ
			|	Справочник.ВнешниеКомпоненты КАК ВнешниеКомпоненты
			|ГДЕ
			|	ВнешниеКомпоненты.Идентификатор В(&Идентификаторы)";
		
		Запрос.УстановитьПараметр("Идентификаторы", ДанныеВнешнихКомпонент.ВыгрузитьКолонку("Идентификатор"));
		
		РезультатЗапроса = Запрос.Выполнить();
		Выборка = РезультатЗапроса.Выбрать();
		
		ИспользуемыеВнешниеКомпоненты = Неопределено;
		
		// Обход результата запроса.
		Для каждого СтрокаРезультата Из ДанныеВнешнихКомпонент Цикл
			
			ПредставлениеКомпоненты = ВнешниеКомпонентыСлужебный.ПредставлениеКомпоненты(
				СтрокаРезультата.Идентификатор, СтрокаРезультата.Версия);
			
			КодОшибки = СтрокаРезультата.КодОшибки;
			
			Если ЗначениеЗаполнено(КодОшибки) Тогда
				
				Если КодОшибки = "АктуальнаяВерсия" Тогда
					РезультатВыполнения.Успех.Вставить(СтрокаРезультата.Идентификатор,
						СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = '%1 - актуальная версия.'"), ПредставлениеКомпоненты));
					Продолжить;
				КонецЕсли;
				
				ИнформацияОбОшибке = "";
				Если КодОшибки = "ОтсутствуетКомпонента" Тогда 
					ИнформацияОбОшибке = НСтр("ru = 'В сервисе отсутствует данная внешняя компонента'");
				ИначеЕсли КодОшибки = "ФайлНеЗагружен" Тогда 
					ИнформацияОбОшибке = НСтр("ru = 'Файл внешней компоненты не загружен из сервиса'");
				КонецЕсли;
				
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось загрузить внешнюю компоненту %1 из сервиса:
					           |%2'"),
					ПредставлениеКомпоненты, ИнформацияОбОшибке);
				
				РезультатВыполнения.Ошибки.Вставить(СтрокаРезультата.Идентификатор, ТекстОшибки);
			
				ЗаписьЖурналаРегистрации(НСтр("ru = 'Обновление внешних компонент'", ОбщегоНазначения.КодОсновногоЯзыка()),
					УровеньЖурналаРегистрации.Ошибка,,,	ТекстОшибки);
				
				Продолжить;
			КонецЕсли;
			
			ДвоичныеДанные = ПолучитьИзВременногоХранилища(СтрокаРезультата.АдресФайла);
			Информация = ВнешниеКомпонентыСлужебный.ИнформацияОКомпонентеИзФайла(ДвоичныеДанные, Ложь);
			
			Если Не Информация.Разобрано Тогда
				
				РезультатВыполнения.Ошибки.Вставить(СтрокаРезультата.Идентификатор, ПредставлениеКомпоненты + " - "
					+ Информация.ОписаниеОшибки + ?(Информация.ИнформацияОбОшибке = Неопределено, "", ": "
					+ ОбработкаОшибок.КраткоеПредставлениеОшибки(Информация.ИнформацияОбОшибке)));

				ЗаписьЖурналаРегистрации(НСтр("ru = 'Обновление внешних компонент'",
					ОбщегоНазначения.КодОсновногоЯзыка()), УровеньЖурналаРегистрации.Ошибка, , ,
					Информация.ОписаниеОшибки);
				Продолжить;
			КонецЕсли;
			
			// Поиск ссылки.
			Отбор = Новый Структура("Идентификатор", СтрокаРезультата.Идентификатор);
			Выборка.Сбросить();
			Если Выборка.НайтиСледующий(Отбор) Тогда 
				// Когда уже загружена компонента по дате более свежая, чем на Портале 1С:ИТС, обновление не следует выполнять.
				Если Выборка.ДатаВерсии > СтрокаРезультата.ДатаВерсии Тогда 
					ПредставлениеКомпоненты = ВнешниеКомпонентыСлужебный.ПредставлениеКомпоненты(
						СтрокаРезультата.Идентификатор, Выборка.Версия);
					
					РезультатВыполнения.Успех.Вставить(СтрокаРезультата.Идентификатор,
						СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = '%1 - в программе более новая версия, чем в сервисе (%2 от %3).'"), 
							ПредставлениеКомпоненты, СтрокаРезультата.Версия, Формат(СтрокаРезультата.ДатаВерсии, "ДЛФ=Д")));
					Продолжить;
				КонецЕсли;
				
				СсылкаНаОбъект = Выборка.Ссылка;
			Иначе
				СсылкаНаОбъект = Неопределено;
				Если ИспользуемыеВнешниеКомпоненты = Неопределено Тогда
					ИспользуемыеВнешниеКомпоненты = ВнешниеКомпонентыСлужебный.ИспользуемыеВнешниеКомпоненты();
				КонецЕсли;
				СтрокаКомпоненты = ИспользуемыеВнешниеКомпоненты.Найти(СтрокаРезультата.Идентификатор, "Идентификатор");
				Если СтрокаКомпоненты <> Неопределено Тогда
					ОбновлятьАвтоматически = СтрокаКомпоненты.ОбновлятьАвтоматически;
				Иначе
					ОбновлятьАвтоматически = Ложь;
				КонецЕсли;
			КонецЕсли;
				
			НачатьТранзакцию();
			Попытка
				
				Блокировка = Новый БлокировкаДанных;
				ЭлементБлокировки = Блокировка.Добавить("Справочник.ВнешниеКомпоненты");
				Если СсылкаНаОбъект <> Неопределено Тогда
					ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаОбъект);
				КонецЕсли;
				Блокировка.Заблокировать();
				
				Если СсылкаНаОбъект <> Неопределено Тогда
					Объект = СсылкаНаОбъект.ПолучитьОбъект();
					Объект.Заблокировать();
				Иначе
					Объект = Справочники.ВнешниеКомпоненты.СоздатьЭлемент();
					Объект.ОбновлятьСПортала1СИТС = ОбновлятьАвтоматически;
				КонецЕсли;
				
				ЗаполнитьЗначенияСвойств(Объект, Информация.Реквизиты); // По данным манифеста.
				ЗаполнитьЗначенияСвойств(Объект, СтрокаРезультата);     // По данным с сайта.
				
				Объект.ЦелевыеПлатформы = Новый ХранилищеЗначения(Информация.Реквизиты.ЦелевыеПлатформы);
				Объект.ДополнительныеСвойства.Вставить("ДвоичныеДанныеКомпоненты", Информация.ДвоичныеДанные);
				
				Объект.ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Загружена с Портала 1С:ИТС. %1.'"),
					ТекущаяДатаСеанса());
				
				Объект.Записать();
				
				РезультатВыполнения.Успех.Вставить(СтрокаРезультата.Идентификатор, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1 - успешно обновлена.'"), ПредставлениеКомпоненты));
				
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				
				РезультатВыполнения.Ошибки.Вставить(СтрокаРезультата.Идентификатор, ПредставлениеКомпоненты
					+ " - " + ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
					
				ЗаписьЖурналаРегистрации(НСтр("ru = 'Обновление внешних компонент'",
						ОбщегоНазначения.КодОсновногоЯзыка()),
					УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			КонецПопытки;
			
		КонецЦикла;
		
		РезультатВыполнения.Результат = РезультатВыполнения.Ошибки.Количество() = 0;
		
		Если ЗначениеЗаполнено(АдресРезультата) Тогда 
			ПоместитьВоВременноеХранилище(РезультатВыполнения, АдресРезультата);
		КонецЕсли;
		
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Действие недоступно, т.к. отсутствует подсистема ""%1"".'"),
			"ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент");
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//      * Идентификатор - Строка
//      * Версия - Строка
//      * ДатаВерсии - Дата
//      * Наименование - Строка
//      * ИмяФайла - Строка
//      * ПутьКФайлу - Строка
//
Функция ОписаниеПоставляемойОбщейКомпоненты() Экспорт
	
	Описание = Новый Структура;
	Описание.Вставить("Идентификатор");
	Описание.Вставить("Версия");
	Описание.Вставить("ДатаВерсии");
	Описание.Вставить("Наименование");
	Описание.Вставить("ИмяФайла");
	Описание.Вставить("ПутьКФайлу");
	Возврат Описание;
	
КонецФункции

// Выполняет обновление общих внешних компонент.
//
// Параметры:
//  ОписаниеКомпоненты - см. ОписаниеПоставляемойОбщейКомпоненты.
//
Процедура ОбновитьОбщуюКомпоненту(ОписаниеКомпоненты) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ВнешниеКомпонентыВМоделиСервиса") Тогда
		МодульВнешниеКомпонентыВМоделиСервисаСлужебный = ОбщегоНазначения.ОбщийМодуль("ВнешниеКомпонентыВМоделиСервисаСлужебный");
		МодульВнешниеКомпонентыВМоделиСервисаСлужебный.ОбновитьОбщуюКомпоненту(ОписаниеКомпоненты);
	КонецЕсли;
	
КонецПроцедуры

// Для версий БИП старше 2.7.2.0 следует использовать ИспользуемыеКомпоненты("ДляОбновления").
// Возвращает таблицу описаний внешних компонент, которые требуется обновлять автоматически с Портала 1С:ИТС.
//
// Возвращаемое значение:
//   см. ПолучениеВнешнихКомпонент.ОписаниеВнешнихКомпонент
//
Функция АвтоматическиОбновляемыеКомпоненты() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент") Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Действие недоступно, т.к. отсутствует подсистема ""%1"".'"),
			"ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент");
	КонецЕсли;
	
	МодульПолучениеВнешнихКомпонент = ОбщегоНазначения.ОбщийМодуль("ПолучениеВнешнихКомпонент");
	ОписаниеВнешнихКомпонент = МодульПолучениеВнешнихКомпонент.ОписаниеВнешнихКомпонент();
	
	ДанныеВнешнихКомпонент = ВнешниеКомпонентыСлужебный.ДанныеВнешнихКомпонент("ДляОбновления");
	
	Для Каждого ОписаниеКомпоненты Из ДанныеВнешнихКомпонент Цикл
		НоваяСтрока = ОписаниеВнешнихКомпонент.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, ОписаниеКомпоненты);
	КонецЦикла;
	
	Возврат ОписаниеВнешнихКомпонент;
	
КонецФункции

#КонецОбласти

// Конец ИнтернетПоддержкаПользователей.ПолучениеВнешнихКомпонент

#КонецОбласти

